const Flow = require('./core/Flow.js')

function deepMerge(obj1, obj2) {
    if (obj1 === undefined) {
        return obj2;
    }
    Object.keys(obj2).forEach(key => {
        if (obj1[key] == undefined) {
            obj1[key] = obj2[key];
        } else if (Array.isArray(obj2[key])) {
            obj1[key] = deepMerge(obj1[key], obj2[key])
        } else if (typeof obj2[key] == 'object' && obj2[key]) {
            obj1[key] = deepMerge(obj1[key] || {}, obj2[key]);
        } else {
            obj1[key] = obj2[key];
        }
    })
    return obj1;
}
function mergeOption(origin, option = {}) {
    origin = deepMerge({}, origin);
    return deepMerge(origin, option);
}

const plugins =  new Set();

class Topo {
    constructor(setting) {
        this.$plugins = new Set()
        this.$setting = {
            ...setting
        };
        this.$name = setting.topo.group + '_' + setting.topo.name;
    }
    create(option) {
        if (option instanceof Flow) {
            option = option.$option;
        }
        const obj = new Flow(this.$setting);
        obj.$option = option;
        if (typeof option === 'function') {
            option = option(obj);
        }
        if (typeof option === 'object') {
            if(!option.name){
                throw new Error('name is required!')
            }
            option.path = this.$name + '_' + option.name;
            obj.$init(option);
            obj.$create = (opt) => {
                if(!opt.name) {
                    throw new Error('name is required');
                }
                if (opt.name == this.$name) {
                    throw new Error('name must be different from the current flow : ' + this.$name);
                }
                const newOption = mergeOption(option, opt);
                newOption.path = this.$name + '_' + (newOption.name);
                const newObj = this.create(newOption);
                return newObj;
            }
            plugins.forEach(plugin => {
                plugin.install(obj);
            })
            this.$plugins.forEach(plugin => {
                plugin.install(obj);
            })
            return obj;
        }
        throw new Error('create(option): 请设置正确的参数')
    }
}

function createTopo(setting) {
    return new Topo(setting);
}
createTopo.use = function(plugin) {
    plugins.add(plugin);
    return createTopo;
}
module.exports = createTopo;
